package com.log4jviewer.ui.preferences.filters;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.log4jviewer.Activator;
import com.log4jviewer.filters.FilterModel;

/**
 * Class represents a builder for filter preference page.
 * 
 * @authors <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a> </br> <a
 *          href="mailto:Daniil.Yaroslavtsev@gmail.com">Daniil Yaroslavtsev</a>
 */
public class FilterPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static Set<FilterPreferencePageListener> listeners = new HashSet<FilterPreferencePageListener>();

    private IPreferenceStore store;

    private FilterController filterController;

    private static FilterContentProvider filterContentProvider;

    private static AbstractFilterSettings filterSettings;

    private static AbstractFilterSettings filterRuleSettings;

    public static FilterContentProvider getFilterContentProvider() {
        return filterContentProvider;
    }

    public static FilterSettings getFilterSettings() {
        return (FilterSettings) filterSettings;
    }

    public static FilterRuleSettings getFilterRulesSettings() {
        return (FilterRuleSettings) filterRuleSettings;
    }

    @Override
    public void init(final IWorkbench workbench) {
        store = Activator.getInstance().getPreferenceStore();
        setPreferenceStore(store);

        filterController = new FilterController();
        filterContentProvider = new FilterContentProvider();

        try {
            filterContentProvider.init(store);

        } catch (IOException e) {
            String errorMessage = "Absolute path for filter config file couldn't be built!";
            setMessage(errorMessage, ERROR);
            setValid(false);
            logger.error(errorMessage, e);

        } catch (ParserConfigurationException e) {
            String errorMessage = "SAX parser for filters' xml config file couldn't be created!";
            setMessage(errorMessage, ERROR);
            setValid(false);
            logger.error(errorMessage, e);

        } catch (SAXException e) {
            String errorMessage = "Error occured when parsing filters' xml config file!";
            setMessage(errorMessage, ERROR);
            setValid(false);
            logger.error(errorMessage, e);
        }
    }

    @Override
    protected Control createContents(final Composite parent) {
        GridLayout gridLayout = new GridLayout();
        gridLayout.verticalSpacing = 15;
        parent.setLayout(gridLayout);

        // Filters' settings (table with available filters, 'Add' filter button, 'Remove' filter button).
        composeFilterSettings(parent);
        logger.info("Filters' settings were composed.");

        // Filter rules' settings (table with filter rules, 'Add' rule button, 'Remove' rule button).
        composeFilterRuleSettings(parent);
        logger.info("Filter rules' settings were composed.");

        // need to select current active filter if preferences window is was opened in first time.
        selectActiveFilter();

        setupListeners();

        logger.info("FilterPreferencePage was created successfully.");

        return parent;
    }

    private void composeFilterSettings(final Composite composite) {
        GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
        Group filterSettingsGroup = createGroupSettings(composite, "Available filters", gridData);
        filterSettings = new FilterSettings(filterController,
                filterContentProvider);
        filterSettings.buildFilterControls(filterSettingsGroup);

    }

    private void composeFilterRuleSettings(final Composite composite) {
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        Group filterRuleSettingsGroup = createGroupSettings(composite, "Filter's rules", gridData);
        filterRuleSettings = new FilterRuleSettings(filterContentProvider,
                filterController);
        filterRuleSettings.buildFilterControls(filterRuleSettingsGroup);
        filterController.setFilterRuleSettings((FilterRuleSettings) filterRuleSettings);
    }

    private void setupListeners() {

        filterSettings.getTableViewer().getTable().addListener(SWT.FocusIn, new Listener() {
            @Override
            public void handleEvent(final Event event) {
                ((FilterRuleSettings) filterRuleSettings).setRemoveButtonEnabled(false);
            }
        });

    }

    private Group createGroupSettings(final Composite composite, final String groupName,
            final GridData gridData) {
        Group groupSettings = new Group(composite, SWT.NONE);
        groupSettings.setText(groupName);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 2;
        groupSettings.setLayout(gridLayout);
        gridData.horizontalSpan = 2;
        groupSettings.setLayoutData(gridData);
        return groupSettings;
    }

    private boolean saveFilterSettingsAndNotifyObservers() {
        boolean isSuccessfulySaved = true;

        try {
            FilterModel activeFilter = filterContentProvider.getActiveFilter();
            notifyListeners(this, getFilterCopy(activeFilter));

            String filtersConfigFilePath = filterContentProvider.getAbsoluteFiltersFilePath();
            List<FilterModel> filterModels = filterContentProvider.getFilters();

            filterController.saveFilters(filtersConfigFilePath, filterModels);
            filterController.saveActiveFilter(store, filterContentProvider.getActiveFilterIndex());

        } catch (IOException e) {
            String errorMessage = "Couldn't open file for saving filters!";
            setMessage(errorMessage, ERROR);
            setValid(false);
            logger.error(errorMessage, e);
            isSuccessfulySaved = false;

        } catch (ParserConfigurationException e) {
            String errorMessage = "Filters' document (DOM model) couldn't be created!";
            setMessage(errorMessage, ERROR);
            setValid(false);
            logger.error(errorMessage, e);
            isSuccessfulySaved = false;
        }

        return isSuccessfulySaved;
    }

    private void selectActiveFilter() {
        ((FilterSettings) filterSettings).setActiveFilterSelection();
        filterSettings.getTableViewer().getTable().notifyListeners(SWT.Selection, new Event());
    }

    private FilterModel getFilterCopy(final FilterModel filter) {
        FilterModel filterCopy = new FilterModel();
        filterCopy.setFilterName(filter.getFilterName());
        filterCopy.setFilterDescr(filter.getFilterDescr());

        for (int i = 0; i < filter.getItemsCount(); i++) {
            if (filter.getItem(i).isEnabled()) {
                filterCopy.addItem(filter.getItem(i));
            }
        }
        return filterCopy;
    }

    public static void notifyListeners(final FilterPreferencePage event,
            final FilterModel filterModel) {
        for (FilterPreferencePageListener listener : listeners) {
            listener.handleFilterPreferenceEvent(event, filterModel);
        }
    }

    public static void addListener(final FilterPreferencePageListener listener) {
        if (listener != null) {
            listeners.add(listener);
        }
    }

    public static void removeListener(final FilterPreferencePageListener listener) {
        listeners.remove(listener);
    }

    @Override
    protected void performApply() {
        super.performApply();
        saveFilterSettingsAndNotifyObservers();
    }

    @Override
    public boolean performOk() {
        super.performOk();
        return saveFilterSettingsAndNotifyObservers();
    }
}
